利用Runtime实现自依赖按钮

context

我在开发中遇到一个问题, 就是传统的Target-Action没法满足我的要求. 因为当按钮触发的瞬间, 控制器可能已经被销毁了. Target没有了. 暂且不说这个设计的优劣, 单纯从技术方面,剖析一下是否可以实现一个按钮 ,这个按钮不依赖外部 Target ,而是依赖自己.

几个想法

首先想到的是继承UIButton,然后给Button增加一个block属性 ,然后内部执行block代码.

但是这样要求我项目中所有使用UIButton的地方都需要修改 ,成本太高,遂放弃.

然后想到通过给UIButton添加Category的方式,完成需求. 之前曾经做个一个分类,就是让NSObject携带一个字典,字典内部可以带上用户自定义的一些参数.解决了一些seletor不能传递任意参数的问题. 所以,这次给Button分类增加个属性,也没有什么问题:

1
2
3
4
5
6
7
8
9
#import <UIKit/UIKit.h>
#import <objc/runtime.h>


typedef void (^ActionCodeBlock)();

@interface UIButton(Block)
- (void) handleControlEvent:(UIControlEvents)controlEvent withBlock:(ActionCodeBlock)action;
@end

定义了一个没有返回值的block作为参数, 然后暴露一个方法供外部调用.

然后 .m 文件

1
2
3
4
5
6
7
8
static char identifier = 'g';

- (void)handleControlEvent:(UIControlEvents)event withBlock:(ActionCodeBlock)codeBlock {

objc_setAssociatedObject(self, &identifier, codeBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
[self addTarget:self action:@selector(callActionCodeBlock:) forControlEvents:event];

}

利用Runtime动态给对象(self)添加一个codeBlock,使用的内存管理机制(关联策略)是OBJC_ASSOCIATION_COPY_NONATOMIC.

关于关联策略,有以下几种:

1
2
3
4
5
6
7
8
9
OBJC_ASSOCIATION_ASSIGN	 @property (assign) or @property (unsafe_unretained)	弱引用关联对象

OBJC_ASSOCIATION_RETAIN_NONATOMIC @property (strong, nonatomic) 强引用关联对象,且为非原子操作

OBJC_ASSOCIATION_COPY_NONATOMIC @property (copy, nonatomic) 复制关联对象,且为非原子操作

OBJC_ASSOCIATION_RETAIN @property (strong, atomic) 强引用关联对象,且为原子操作

OBJC_ASSOCIATION_COPY @property (copy, atomic) 复制关联对象,且为原子操作

有开发经验的人都明白其中的意思,在此不再赘述.

然后, 给自己添加了一个Target-Action

1
[self addTarget:self action:@selector(callActionCodeBlock:) forControlEvents:event];

最后,实现 selector,代码如下:

1
2
3
4
5
6
- (void)callActionCodeBlock:(id)sender {
ActionCodeBlock codeBlock = (ActionCodeBlock)objc_getAssociatedObject(self, &identifier);
if (codeBlock) {
codeBlock();
}
}

通过 objc_getAssociatedObject 拿到self中的 identifier为g 的对象(block),然后如果block存在,则执行.